/*
 * ============================================================================
 *
 *       Filename:  QQWryCompress.cpp
 *
 *    Description:  Defination of QQWryCompress class
 *
 *         Author:  liangqing
 *
 * ============================================================================
 */
#include "QQWryCompress.h"
#include <iterator>
namespace {
    inline void int2bytes(int integer, char *bytes, int size=3){
        bytes[0] = integer & 0xFF;
        bytes[1] = (integer & 0xFF00) >> 8;
        bytes[2] = (integer & 0xFF0000) >> 16;
        if(size > 3)
            bytes[3] = (integer &0xFF000000) >> 24;
    }
}

QQWryCompress::QQWryCompress(const char *filename) :
        _qqwry(filename, std::ios::binary),
        _count(0), _file_size(0) {
    //_qqwry = fopen(filename, "wb");
    //setvbuf(_qqwry, NULL, _IOFBF, 4096);
    _file_size += 8;
    _seek(8);
    _buf = new char[4096];
    _sbuf = _qqwry.rdbuf();
    _sbuf->pubsetbuf(_buf, 4096);
}

int QQWryCompress::_write(const char *buf, int size) {
    //return fwrite(buf, size, 1, _qqwry);
    return _sbuf->sputn(buf, size);
}

void QQWryCompress::_put(const char c) {
    //fputc(c, _qqwry);
    _sbuf->sputc(c);
}

void QQWryCompress::_seek(int pos) {
    //fseek(_qqwry, pos, SEEK_SET);
    _qqwry.seekp(pos, std::ios::beg);
}

int QQWryCompress::_output_offset(int integer, char *offset, int size) {
    int2bytes(integer, offset, size);
    _write(offset, size);
    return size;
}

bool QQWryCompress::_push_index(Index &index) {
#ifdef USE_SET
    return _indexes.insert(index).second;
#else
    _indexes.push_back(index);
    return true;
#endif /* USE_SET */
}

bool QQWryCompress::put(IPRecord *record) {
    char offset[3];

    Index idx;

    ResourceMap::iterator str_find_region;
    ResourceMap::iterator str_find_section;
    StrMap::iterator section_find;
    StrResource rec ;
    
    memcpy(idx.ip, record->begin_ip, sizeof(record->begin_ip));
#ifdef USE_SET
    memcpy(idx._ip, record->end_ip, sizeof(record->end_ip));
#endif /* USE_SET */
    int2bytes(_file_size, idx.offset);
    if(!_push_index(idx)) return true;

    _count++;
    
    _write((char*)record->end_ip, sizeof(record->end_ip));
    _file_size += sizeof(record->end_ip);
    str_find_region = _strings.find(record->region);
    
    if(str_find_region != _strings.end()) { //region is found
        if((section_find = str_find_region->second.sections.find(record->section))
            != str_find_region->second.sections.end()) {
            //region and section are found in a same resource 
            _put(0x1);
            _file_size += 1;

            //store region and section offset
            _file_size += _output_offset(section_find->second, offset);
        }
        else {
            str_find_region->second.sections[record->section] = _file_size;
            _put(0x2);
            _file_size += 1;
          
            //store region offset
            _file_size += _output_offset(str_find_region->second.offset, offset);
            
            if(strlen(record->section) > 3 && (str_find_section=_strings.find(record->section))
                != _strings.end()) {   //section is found in other resource 
                _put(0x2);
                _file_size += 1;

                //store section offset
                _file_size += _output_offset(str_find_section->second.offset, offset);
            }
            else {  //section is not found in any resource 
                StrResource tr = { _file_size };
                _strings[record->section] = tr;

                //store section string
                //_qqwry << record->section << '\0';
                _write(record->section, strlen(record->section)+1);
                _file_size += strlen(record->section) + 1;
            }
        }
    }
    else {  //region is not found
        rec.offset = _file_size;
        rec.sections.clear();
        
        //store region string
        //_qqwry << record->region << '\0';
        _write(record->region, strlen(record->region)+1);
        _file_size += strlen(record->region) + 1;

        rec.sections[record->section] = rec.offset;

        if(strlen(record->section) > 3 && (str_find_region = _strings.find(record->section))
            !=_strings.end()) {  //section is found
            _put(0x2);
            _file_size += 1;

            //store section offset
            _file_size += _output_offset(str_find_region->second.offset, offset);
        }
        else {  //both region and section are not found
            StrResource tr = { _file_size };
            _strings[record->section] = tr;

            //store section string
            //_qqwry << record->section << '\0';
            _write(record->section, strlen(record->section)+1);
            _file_size += strlen(record->section) + 1;
        }
        _strings[record->region] = rec;
    }
    return true;
}

bool QQWryCompress::finish() {
    int first_index, last_index;
    char offset[4];

#ifndef USE_SET
    std::sort(_indexes.begin(), _indexes.end());
#endif /* USE_SET */

    first_index = _file_size;
    
    _file_size += (Index::size * _indexes.size());
    //It's slowly than below
    //copy(_indexes.begin(), _indexes.end(), std::ostream_iterator<Index>(_qqwry));
    
    IndexContainer::iterator index_itr;
    for(index_itr = _indexes.begin(); index_itr != _indexes.end(); ++index_itr) {
        _write( (char *) ((*index_itr).ip), sizeof((*index_itr).ip) );
        _write( (*index_itr).offset, sizeof((*index_itr).offset) );
    }

    last_index = _file_size - Index::size;
    
    //change header value
    _seek(0);
    _output_offset(first_index, offset, sizeof(offset));
    _output_offset(last_index, offset, sizeof(offset));

    return true;
}

